import tkinter as tk
from tkinter import Canvas, filedialog, messagebox, Listbox
from PIL import Image, ImageTk, ImageDraw
import os
import re
import json
#import numpy as np
import time
import math
#import threading
from openpyxl import load_workbook, Workbook
from openpyxl.drawing.image import Image as openpyxlImage
from openpyxl.styles import Alignment
import datetime
from datetime import datetime


PYDIR = os.path.dirname(os.path.abspath(__file__))
INIT_FILE =  os.path.join(PYDIR, "data", "init_data.txt")#默认的初始化文件路径
INIT_DICT = {#备用的默认数值，如果文件INIT_FILE = "data\init_data.txt"丢失时使用。这些数据为常量
    'FONTS_DIR': PYDIR +'\\data\\fonts',
    'RADICALS_DIR':PYDIR +'\\data\\radicals',
    'MARKS_DIR':PYDIR +'\\data\\marks',
    'DOCUMENTS_DIR':PYDIR +'\\data\\documents',
    'EXCEL_FILE':PYDIR +'\\data\\excel_output.xlsx',

    'D_SAVE_WIDTH':60,
    'D_SAVE_HEIGHT':120,
    'D_MAXBYTE':6,
    'D_CANVAS_WIDTH':180,
    'D_CANVAS_HEIGHT':360,
    'D_BRUSH_SIZE':9,
    'D_ERASER_SIZE':40,

    'M_SCROOL_WIDTH':150,
    
    'T_IMG_SIZE_MULTIPLE':0.5,
    'T_CANVAS_WIDTH':785,
    'T_CANVAS_HEIGHT':400,
    'T_CANVAS_MARGIN':0,
    'T_PERSIST_MATH_SIZE':32,
    'T_PERSIST_MARGIN':30,
    'T_MAX_PERSIST_PINYIN_LENGTH':15
    }
WORDSDATA_FILE = os.path.join(PYDIR, "data", "words_data.json")
WORDSDATA_DICT = {#备用的默认数值，如果文件WORDSDATA_FILE = "data\words_data.json"丢失时使用。这些数据为会随着使用改变的数据，不允许随意改
	"SUM_OF_WORDS": 0, 
	"SUM_OF_FONTS": 0, 
	"SUM_OF_RADICALS": 0, 
	"SUM_OF_MARKS":0
}

def Appto(nowroot, gotoapp):
    nowroot.destroy()
    gotoroot=tk.Tk()
    app=gotoapp(gotoroot)
    gotoroot.mainloop()

def assign_only_None(var, value = None):
    if var is None:
        var = value
        return True
    else:
        return False

class IMG_MIXER:#用于Mixer类
    def __init__(self, imgpath : str, imgpinyin : str, rm_index, isradical : bool = False):
        self.path=imgpath
        self.pinyin=imgpinyin
        self.x = 0#打印时左上角坐标
        self.y = 0
        self.originalsize = (None, None)#原图大小
        self._xsizemul = 1
        self._ysizemul = 1
        self._rotatenum = 0
        self.isshow = False
        self.isradical = isradical
        self.rm_index = rm_index#r/m开头+数字
    def imgopen(self) -> Image:
        img=Image.open(self.path)
        self.originalsize = img.size
        if self._rotatenum != 0:
            img=img.rotate(self._rotatenum, expand=True)
        if self._xsizemul != 1 or self._ysizemul != 1:
            img=img.resize((int(self._xsizemul*img.size[0]), int(self._ysizemul*img.size[1])))
        return img
    def setsizemul(self, xsizemul = None, ysizemul = None):
        if self.originalsize == (None, None):
            self.originalsize = Image.open(self.path).size
        if xsizemul is not None:
            if xsizemul < 1 /self.originalsize[0]:
                xsizemul = 1 / self.originalsize[0]#防止图片过小
            self.x -= int((xsizemul - self._xsizemul) * self.originalsize[0] / 2)#调整x坐标，以保持居中
            self._xsizemul = xsizemul
        if ysizemul is not None:
            if ysizemul < 1 /self.originalsize[1]:
                ysizemul = 1 / self.originalsize[1]
            self.y -= int((ysizemul - self._ysizemul) * self.originalsize[1] / 2)
            self._ysizemul = ysizemul
    def setrotatenum(self, num : int):#旋转图片，num为角度（角度制),正数为顺时针，负数为逆时针
        if self.originalsize == (None, None):
            self.originalsize = Image.open(self.path).size
        num = num % 360
        ori_x_offset, ori_y_offset = self._rotate_xy_offset_get(self._rotatenum, self.originalsize[0], self.originalsize[1])
        new_x_offset, new_y_offset = self._rotate_xy_offset_get(num, self.originalsize[0], self.originalsize[1])
        self.x += int(new_x_offset - ori_x_offset)#调整x坐标，以保持居中
        self.y += int(new_y_offset - ori_y_offset)
        self._rotatenum = num #角度制，用于其他函数使用
    def _rotate_xy_offset_get(self, degree, imgw, imgh):#计算旋转后图片左上角坐标偏移
        if degree >= 0 and degree <= 90:
            pass#degree = degree - 0
        elif degree > 90 and degree <= 180:
            degree = 180 - degree
        elif degree > 180 and degree <= 270:
            degree = degree - 180
        else:#270-360
            degree = 360 - degree
        rad = math.radians(degree)
        x_offset_get = (imgw / 2) - ((math.sin(rad) * imgh / 2) + (math.cos(rad) * imgw / 2))
        y_offset_get = (imgh / 2) - ((math.cos(rad) * imgh / 2) + (math.sin(rad) * imgw / 2))
        return x_offset_get, y_offset_get
    @property
    def xy(self):
        return (self.x, self.y)




    
class MainApp:
    def __init__(self, root):
        #首页，她是一个窗口
        #with open(WORDSDATA_FILE, 'r', encoding='utf-8') as f:
        #    WORDSDATA_DICT.update(json.load(f))
        font20=(None, 20, 'bold')
        font15=(None, 15, 'bold')
        self.root = root
        self.root.title("碱合语库")
        self.root.geometry("560x450")
        self.frame = tk.Frame(root, bg='lightyellow')
        self.frame.pack(fill=tk.BOTH, expand=True)
        tk.Message(self.frame, text=f"已创造{WORDSDATA_DICT['SUM_OF_WORDS']}字+部！", bg='lightyellow', font=font20, width=300).grid(row=0, columnspan=4, padx=15)
        tk.Message(self.frame, text=f"--{WORDSDATA_DICT['SUM_OF_FONTS']}单字 {WORDSDATA_DICT['SUM_OF_RADICALS']}部首 {WORDSDATA_DICT['SUM_OF_MARKS']}附标",
                    bg='lightyellow', font=font15, width=300).grid(row=1, column=1, columnspan=4, padx=15)
        tk.Button(self.frame, text="绘制文字", command=lambda:Appto(self.root, DrawingApp), height=3, width=10, font= font20).grid(row=2, column=0, columnspan=2, padx=60, pady=40)
        tk.Button(self.frame, text="部首合字", command=lambda:Appto(self.root, Mixer), height=3, width=10, font= font20).grid(row=2, column=2, columnspan=2, padx=60, pady=40)
        tk.Button(self.frame, text="文字作文", command=lambda:Appto(self.root, TextEditor), height=3, width=12, font= font20).grid(row=3, column=0, columnspan=3, padx=40, sticky=tk.E)
        tk.Button(self.frame, text="退出", command=lambda:root.destroy()).grid(row=4, column=0, padx=15, pady=60, sticky=tk.SW)
        tk.Button(self.frame, text="Excel转换", command=lambda:Appto(self.root, ListApp)).grid(row=4, column=2, padx=15, pady=60, sticky=tk.EW)
        tk.Button(self.frame, text="设置", command=lambda:Appto(self.root, Setting)).grid(row=4, column=3, padx=15, pady=60, sticky=tk.EW)
            
class DrawingApp:
    def __init__(self, root):
        #一个窗口，包含一个画布和按钮。可以绘制和保存图片
        self.root = root
        self.root.title("文字绘制")
        self.MAXBYTE = INIT_DICT['D_MAXBYTE']
        self.CANVAS_WIDTH = INIT_DICT["D_CANVAS_WIDTH"]
        self.CANVAS_HEIGHT = INIT_DICT["D_CANVAS_HEIGHT"]
        self.bytelength = max(min(self.MAXBYTE, 2), 1)
        self.canvas = Canvas(root, width = self.bytelength * self.CANVAS_WIDTH, height=self.CANVAS_HEIGHT, bg='white')
        self.canvas.pack(padx=10, pady=10, anchor=tk.NW)
        self.current_image = Image.new('RGB', (self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT), "white")
        self.guidelines_image = Image.new('RGBA', (self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT), (255, 255, 255, 0))#用于辅助线的绘制
        self.guidelines_type = 0
        self.GUIDELINES_TYPE_MAX = 6
        self.draw_of_guidelines = ImageDraw.Draw(self.guidelines_image)
        self.draw = ImageDraw.Draw(self.current_image)
        #按钮方法。待添加：cut,drag,undo
        tk.Button(root, text="返回", command=lambda:Appto(self.root, MainApp)).pack(side=tk.LEFT, padx=10, pady=10)
        tk.Button(root, text="打开", command=self.open_image).pack(side=tk.LEFT, padx=10, pady=10)
        tk.Button(root, text="保存(s)", command=self.open_save_popup).pack(side=tk.LEFT, padx=10, pady=10)
        root.bind("<KeyPress-s>", self.open_save_popup)
        tk.Button(root, text="清屏(c)", command=self.clear_canvas).pack(side=tk.LEFT, padx=10, pady=10)
        root.bind("<KeyPress-c>", self.clear_canvas)
        tk.Button(root, text="辅助线切换(t)", command=lambda:self.guidelines_update(switchtype = True)).pack(side=tk.LEFT, padx=10, pady=10)
        root.bind("<KeyPress-t>", lambda e:self.guidelines_update(switchtype = True))
        tk.Button(root, text="变长1字节(a)", command=self.lengthadd_canvas).pack(side=tk.LEFT, padx=10, pady=10)
        root.bind("<KeyPress-a>", self.lengthadd_canvas)
        tk.Button(root, text="变短1字节(d)", command=self.lengthminus_canvas).pack(side=tk.LEFT, padx=10, pady=10)
        root.bind("<KeyPress-d>", self.lengthminus_canvas)
        
        self.old_x = None
        self.old_y = None
  
        self.canvas.bind("<Button-1>", self.on_draw_begin1)  
        self.canvas.bind("<B1-Motion>", self.on_draw_move1)
        self.canvas.bind("<Button-3>", self.on_draw_begin3)
        self.canvas.bind("<B3-Motion>", self.on_draw_move3)
        
        self.update_canvas(True)
        
    def on_draw_begin1(self, event):  
        self.old_x = event.x
        self.old_y = event.y
  
    def on_draw_move1(self, event):  
        if self.old_x and self.old_y:
            lineid =  self.draw.line([(self.old_x, self.old_y), (event.x, event.y)], fill="black", width=INIT_DICT['D_BRUSH_SIZE'])  
            self.old_x, self.old_y = event.x, event.y
            self.update_canvas()

    def on_draw_begin3(self, event):  
        self.old_x = event.x  
        self.old_y = event.y 
        #self.draw.point([event.x, event.y], fill="white")
  
    def on_draw_move3(self, event):
        if self.old_x and self.old_y:
            self.draw.line([(self.old_x, self.old_y), (event.x, event.y)], fill="white", width=INIT_DICT['D_ERASER_SIZE'])#eraser
            self.old_x, self.old_y = event.x, event.y
            self.update_canvas()
        
  
    def update_canvas(self, guidelines_update=False):
        if guidelines_update:
            self.guidelines_update(False)
        self.openedimages = []
        self.draw = ImageDraw.Draw(self.current_image)
        photo2 = ImageTk.PhotoImage(self.current_image)
        self.canvas.create_image(0, 0, image=photo2, anchor=tk.NW)
        self.openedimages.append(photo2)  # 防止图像被垃圾回收

        self.draw_of_guidelines = ImageDraw.Draw(self.guidelines_image)
        photo1 = ImageTk.PhotoImage(self.guidelines_image)
        self.canvas.create_image(0, 0, image=photo1, anchor=tk.NW)
        self.openedimages.append(photo1)  # 防止图像被垃圾回收
  
    def open_save_popup(self, none = None): #在此弹窗输入拼音即可保存图片
        popup = tk.Toplevel()
        popup.title("输入拼音后按回车键保存")
        entry = tk.Entry(popup)
        entry.pack()
        var_to_word = tk.IntVar(value=True)
        var_to_radical = tk.IntVar()
        var_to_mark = tk.IntVar()
        tk.Checkbutton(popup, text="成为单字(Alt+q)", variable=var_to_word).pack()
        tk.Checkbutton(popup, text="成为部首(Alt+w)", variable=var_to_radical).pack()
        tk.Checkbutton(popup, text="成为附标(Alt+e)", variable=var_to_mark).pack()
        extrainfo = "按Enter确定保存\n单字:你可以直接使用\n部首:你可以稍后融合部首，以更快造字\n附标:你可以在部首融合处使用，也可以在作文时输入到上一个单词的头顶或底下等。通常，保存为附标时，你不需再保存为单字"
        tk.Button(popup, text="??", command=lambda: messagebox.showinfo("按回车键(Enter)保存", extrainfo)).pack(side=tk.RIGHT, pady=10)
        tk.Button(popup, text="取消(Alt+F4)", command=popup.destroy).pack(side=tk.LEFT, pady=10)
        entry.bind("<Return>", lambda e: self.save_image(entry, popup, var_to_word, var_to_radical, var_to_mark))
        var_invert = lambda var:var.set(not var.get())#翻转var的勾选
        entry.bind("<Alt-q>", lambda f: var_invert(var_to_word))
        entry.bind("<Alt-w>", lambda f: var_invert(var_to_radical))
        entry.bind("<Alt-e>", lambda f: var_invert(var_to_mark))
        entry.focus_set()# 自动将焦点设置到输入框上
        popup.mainloop()

    def save_image(self, entry, popup, vartoword, vartoradical, vartomark):
        imgpinyin = entry.get()
        istoward = vartoword.get()
        istoradical = vartoradical.get()
        istomark = vartomark.get()
        if not (istoward or istoradical or istomark):
            messagebox.showinfo(message="请至少选择一项\n按'??'查看帮助")
        elif not imgpinyin:
            messagebox.showinfo(message="请输入拼音\n单字建议用英文字母,部首和附标则可以用你的母语")
        elif '+' in imgpinyin:
            messagebox.showinfo(message="请不要使用'+'!")
        else:
            save_image = self.current_image.crop([0, 0, self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT])
            transparent_save_image = None
            if istoradical or istomark:
                # 保存为透明图
                transparent_save_image = Image.new("RGBA", save_image.size, (255, 255, 255, 0))
                datas=save_image.getdata()
                newData=[]
                for item in datas:#将白色像素转换为透明
                    if item[0] == 255 and item[1] == 255 and item[2] == 255:
                        newData.append((255, 255, 255, 0))
                    else:
                        newData.append((item[0], item[1], item[2], 255))
                transparent_save_image.putdata(newData)
            if istoward:
                r_save_image = save_image.resize([self.bytelength * INIT_DICT["D_SAVE_WIDTH"], INIT_DICT["D_SAVE_HEIGHT"]])
                #保存格式：'拼音'+"+"+'字节长度（一位数）'(+"_第几个同字节该拼音字")+',你想补充的信息*仅限v1.0.0以前版本'
                file_path = INIT_DICT['FONTS_DIR'] + '\\' + imgpinyin + '+' + str(self.bytelength)
                if not os.path.exists(file_path + '.png'):
                    r_save_image.save(file_path + '.png')
                    WORDSDATA_DICT["SUM_OF_FONTS"]+=1
                    WORDSDATA_DICT["SUM_OF_WORDS"]+=1
                    popup.destroy()
                else:#处理拼音相同的情况：用"-"+'是第几个该拼音的字'区别。后期会大改这种按名字索引的方法
                    for i in range(2, 32767):
                        file_path_extra = file_path + '-' + str(i) + '.png'
                        if not os.path.exists(file_path_extra):
                            r_save_image.save(file_path_extra)
                            WORDSDATA_DICT["SUM_OF_FONTS"]+=1
                            WORDSDATA_DICT["SUM_OF_WORDS"]+=1
                            popup.destroy()
                            break
            if istoradical:
                #会保存成部首
                file_path = INIT_DICT["RADICALS_DIR"] + '\\' + imgpinyin
                try:
                    os.makedirs(file_path, exist_ok=False)
                    transparent_save_image.save(file_path + '\\' + 'img.png')
                    with open(file_path + '\\' + 'data.txt', "w", encoding='utf-8') as file:
                        file.write(f"{imgpinyin}+{self.bytelength}:")
                    WORDSDATA_DICT["SUM_OF_RADICALS"]+=1
                    WORDSDATA_DICT["SUM_OF_WORDS"]+=1
                    popup.destroy()
                except FileExistsError:
                    messagebox.showerror("该名称已被取", "部首不支持重名，请换个名字或删除原部首")
                except Exception as e:
                    messagebox.showerror('Create Failed', e)
            if istomark:
                #会保存成附标
                file_path = INIT_DICT["MARKS_DIR"] + '\\' + imgpinyin
                try:
                    os.makedirs(file_path, exist_ok=False)
                    transparent_save_image.save(file_path + '\\' + 'img.png')
                    with open(file_path + '\\' + 'data.txt', "w", encoding='utf-8') as file:
                        file.write(f"{imgpinyin}+{self.bytelength}:")
                    WORDSDATA_DICT["SUM_OF_MARKS"]+=1
                    WORDSDATA_DICT["SUM_OF_WORDS"]+=1
                    popup.destroy()
                except FileExistsError:
                    messagebox.showerror("该名称已被取", "附标不支持重名，请换个名字或删除原附标")
                except Exception as e:
                    messagebox.showerror('Create Failed', e)
            with open(WORDSDATA_FILE, "w", encoding='utf-8') as f:
                json.dump(WORDSDATA_DICT, f)

    def open_image(self):  
        file_path = filedialog.askopenfilename(filetypes=[("PNG files", "*.png"), ("All files", "*.*")], initialdir=INIT_DICT['FONTS_DIR'])  
        if file_path:
            img = Image.open(file_path)
            #得到长度
            imglength = file_path.rsplit('+', 1)[1][0]
            if imglength >= '1' and imglength <= '9':
                imglength = int(imglength)
            else:
                imglength=max(min(int(img.width / img.height), INIT_DICT["D_MAXBYTE"]), 1)#备用方案
            self.canvas.config(width=imglength * self.CANVAS_WIDTH)
            resize_img = img.resize([imglength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT])
            #self.current_image.paste(resize_img, (0,0))
            self.current_image = resize_img
            self.bytelength = imglength
            self.update_canvas()   
   
    def clear_canvas(self, none = None):  
        self.draw.rectangle([0, 0, self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT], fill="white")
        self.update_canvas()

    def guidelines_update(self, switchtype = True):
        if switchtype == True:
            if(self.guidelines_type < self.GUIDELINES_TYPE_MAX - 1):
                self.guidelines_type += 1
            else:
                self.guidelines_type = 0
        now_height = self.CANVAS_HEIGHT
        now_width = self.CANVAS_WIDTH * self.bytelength
        self.guidelines_image = Image.new("RGBA", [now_width, now_height], (255, 255, 255, 0))
        self.draw_of_guidelines = ImageDraw.Draw(self.guidelines_image)
        if self.guidelines_type == 0:#None
            pass
        elif self.guidelines_type == 1:#矩形框
            self.draw_of_guidelines.rectangle([10, 10, now_width - 10, now_height - 10], outline='grey', width=2)
        elif self.guidelines_type == 2:#四线格
            for i in range(0, 4):
                mul = i + 0.05
                self.draw_of_guidelines.line([(5, int(mul * now_height / 3.1)), (now_width - 5, int(mul * now_height / 3.1))], fill='grey', width=2)
        elif self.guidelines_type == 3:#字节分割
            for i in range(1, self.bytelength):
                self.draw_of_guidelines.line([(i * self.CANVAS_WIDTH, 0), (i * self.CANVAS_WIDTH, now_height)], fill='grey', width=2)
        elif self.guidelines_type == 4:#田字格，有点卡
            self.draw_of_guidelines.rectangle([4, 4, now_width - 4, now_height - 4], outline='grey', width=2)
            lines = (((4, 4), (now_width - 4, now_height - 4)),#NW_to_SE
                     ((now_width - 4, 4),(4, now_height - 4)),#NE_to_SW
                     (((now_width + 4) // 2, 4), ((now_width + 4) // 2, now_height - 4)),#LC_to_RC
                     ((4, (now_height + 4) // 2), (now_width - 4, (now_height + 4) // 2)),#TC_to_BC
            )
            for line in lines:
                self.draw_of_guidelines.line(line, fill='grey', width=2)
        elif self.guidelines_type == 5:#网格
            for i in range(0, now_height // 16):
                self.draw_of_guidelines.line([(0, (i + 1) * now_height / 16), (now_width, (i + 1) * now_height / 16)], fill='grey', width=2)
            for j in range(0, now_width // 16):
                self.draw_of_guidelines.line([((j + 1) * now_width / 16, 0), ((j + 1) * now_width / 16, now_height)], fill='grey', width=2)


        self.update_canvas()
        
    def lengthadd_canvas(self, none = None):
        if self.bytelength < self.MAXBYTE:
            self.bytelength+=1
            img = self.current_image
            self.current_image = Image.new("RGB", [self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT], "white")
            self.current_image.paste(img, (0, 0))
            self.draw.rectangle([(self.bytelength - 1) * self.CANVAS_WIDTH, 0, self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT], fill="white")
            self.canvas.config(width = self.bytelength * self.CANVAS_WIDTH)
            self.update_canvas(True)
        
    def lengthminus_canvas(self, none = None):
        if self.bytelength > 1:
            self.bytelength-=1
            self.current_image = self.current_image.crop([0, 0, self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT])
            self.canvas.config(width = self.bytelength * self.CANVAS_WIDTH)  
            self.update_canvas(True)
        
class Mixer:
    def __init__(self, root):
        #一个窗口，用来混合部首和附标
        self.root = root
        self.root.title("部首混合")
        self.MAXBYTE = INIT_DICT['D_MAXBYTE']
        self.CANVAS_WIDTH = INIT_DICT["D_CANVAS_WIDTH"]
        self.CANVAS_HEIGHT = INIT_DICT["D_CANVAS_HEIGHT"]
        self.SCROLL_WIDTH = INIT_DICT["M_SCROOL_WIDTH"]
        self.bytelength = max(min(self.MAXBYTE, 2), 1)
        self.imgs = []#所有部首和附标数据
        self.loaddata()#加载图片数据
        self.old_x = 0
        self.old_y = 0
        #勾选框r(for radicals)
        self.r_canvas_frame = tk.Frame(self.root)
        self.r_canvas_frame.grid(row=0, column=0,columnspan=2, sticky=tk.N)
        self.r_canvas_scroll = tk.Scrollbar(self.r_canvas_frame, orient=tk.VERTICAL)
        self.r_canvas_scroll.pack(side=tk.LEFT, fill=tk.Y)
        
        self.r_canvas = tk.Canvas(self.r_canvas_frame, bd=0, highlightthickness=0,
                        yscrollcommand=self.r_canvas_scroll.set, width=self.SCROLL_WIDTH)
        self.r_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
        
        self.r_canvas_scroll.config(command=self.r_canvas.yview)
        self.r_canvas.create_text(0, 0, text="部首列表", anchor=tk.NW)
        self.r_y_offset = 20
        for img in self.imgs:
            if img.isradical:
                _,_ = self.add_checkbox(self.r_canvas, img.pinyin, img.path, img.rm_index, 0, self.r_y_offset)
                self.r_y_offset += 33
        self.r_canvas.config(scrollregion=(0, 0, 0, self.r_y_offset))
        #勾选框m(for marks)
        self.m_canvas_frame = tk.Frame(self.root)
        self.m_canvas_frame.grid(row=0, column=2, columnspan=2, sticky=tk.N)
        self.m_canvas_scroll = tk.Scrollbar(self.m_canvas_frame, orient=tk.VERTICAL)
        self.m_canvas_scroll.pack(side=tk.LEFT, fill=tk.Y)
        
        self.m_canvas = tk.Canvas(self.m_canvas_frame, bd=0, highlightthickness=0,
                        yscrollcommand=self.m_canvas_scroll.set, width=self.SCROLL_WIDTH)
        self.m_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
        
        self.m_canvas_scroll.config(command=self.m_canvas.yview)
        self.m_canvas.create_text(0, 0, text="副标列表", anchor=tk.NW)
        self.m_y_offset = 20
        for img in self.imgs:
            if not img.isradical:
                _,_ = self.add_checkbox(self.m_canvas, img.pinyin, img.path, img.rm_index, 0, self.m_y_offset)
                self.m_y_offset += 33
        self.m_canvas.config(scrollregion=(0, 0, 0, self.m_y_offset))
        #备选框,n。将勾选的部首和副标显示在这里，方便用户选择以进一步处理
        self.n_img_num = 0#用于记录已选中的img的个数
        self.n_index_to_canvasid = {}#用id访问，以删除n_canvas中的组件
        self.n_canvas_frame = tk.Frame(self.root)
        self.n_canvas_frame.grid(row=1, column=0, columnspan=3, sticky=tk.NW)
        self.n_canvas = tk.Canvas(self.n_canvas_frame, bd=0, highlightthickness=0, width=int(self.SCROLL_WIDTH * 2))
        self.n_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
        self.n_canvas.create_text(0, 0, text="下为已选定的，取消勾选以隐藏：\n*右侧操作的对象是你最后一次在此框点击的", anchor=tk.NW)
        self.lastclick_imgindex = None#用于记录上次点击的img，以进一步操作
        #c,config调整选定img的属性
        self.c_canvas_frame = tk.Frame(self.root)
        self.c_canvas_frame.grid(row=1, column=3, sticky=tk.NW)
        tk.Message(self.c_canvas_frame, text="x+=", anchor=tk.NW).grid(row=0, column=0)
        self.c_entry_x = tk.Entry(self.c_canvas_frame, width=3, name='c_entry_x')
        self.c_entry_x.grid(row=1, column=0)
        self.c_entry_x.bind("<Return>", lambda e:self.change_img_pos(self.c_entry_x))
        tk.Message(self.c_canvas_frame, text="y+=", anchor=tk.NW).grid(row=0, column=1)
        self.c_entry_y = tk.Entry(self.c_canvas_frame, width=3, name='c_entry_y')
        self.c_entry_y.grid(row=1, column=1)
        self.c_entry_y.bind("<Return>", lambda e:self.change_img_pos(self.c_entry_y))
        tk.Message(self.c_canvas_frame, text="旋=", anchor=tk.NW).grid(row=2, column=0)
        self.c_entry_o = tk.Entry(self.c_canvas_frame, width=3, name='c_entry_o')
        self.c_entry_o.grid(row=3, column=0)
        self.c_entry_o.bind("<Return>", lambda e:self.change_img_pos(self.c_entry_o))
        tk.Message(self.c_canvas_frame, text="大=", anchor=tk.NW).grid(row=2, column=0)
        self.c_entry_s = tk.Entry(self.c_canvas_frame, width=3, name='c_entry_s')
        self.c_entry_s.grid(row=3, column=0)
        self.c_entry_s.bind("<Return>", lambda e:self.change_img_pos(self.c_entry_s))
        tk.Message(self.c_canvas_frame, text="x大=", anchor=tk.NW).grid(row=4, column=0)
        self.c_entry_sx = tk.Entry(self.c_canvas_frame, width=3, name='c_entry_sx')
        self.c_entry_sx.grid(row=5, column=0)
        self.c_entry_sx.bind("<Return>", lambda e:self.change_img_pos(self.c_entry_sx))
        tk.Message(self.c_canvas_frame, text="y大=", anchor=tk.NW).grid(row=4, column=1)
        self.c_entry_sy = tk.Entry(self.c_canvas_frame, width=3, name='c_entry_sy')
        self.c_entry_sy.grid(row=5, column=1)
        self.c_entry_sy.bind("<Return>", lambda e:self.change_img_pos(self.c_entry_sy))



        #画布
        self.canvas = Canvas(root, width = self.bytelength * self.CANVAS_WIDTH, height=self.CANVAS_HEIGHT, bg='white')  
        self.canvas.grid(row=0, column=4, rowspan=4)
        tk.Button(root, text="返回", command=lambda:Appto(self.root, MainApp)).grid(row=4, column=0, sticky=tk.W)
        tk.Button(root, text="保存(s)", command=self.open_save_popup).grid(row=4, column=1, sticky=tk.W)
        root.bind("<KeyPress-s>", self.open_save_popup)
        tk.Button(root, text="清屏(c)", command=self.clear_canvas).grid(row=4, column=2, sticky=tk.W)
        root.bind("<KeyPress-c>", self.clear_canvas)
        tk.Button(root, text="变长1字节(a)", command=self.lengthadd_canvas).grid(row=4, column=3, sticky=tk.W)
        root.bind("<KeyPress-a>", self.lengthadd_canvas)
        tk.Button(root, text="变短1字节(d)", command=self.lengthminus_canvas).grid(row=4, column=4, sticky=tk.W)
        tk.Message(root, text="长按鼠标左键拖动，长按鼠标右键拖动旋转，鼠标滚轮缩放", anchor=tk.NW).grid(row=4, column=4, sticky=tk.E)
        root.bind("<KeyPress-d>", self.lengthminus_canvas)
        self.update_canvas()
        self.canvas.bind("<Button-1>", self.on_click13)
        self.canvas.bind("<Button-3>", self.on_click13)
        self.canvas.bind("<MouseWheel>", self.on_scroll)
        self.canvas.bind("<ButtonRelease-1>", self.on_click1_release)
        self.canvas.bind("<ButtonRelease-3>", self.on_click3_release)
        
    def on_checkbox_changed(self, checkvar):#当勾选框状态改变时，更新n_canvas或canvas
        target_index = checkvar._name#(n)+r/m+indexnum(每个图片r/m+indexnum唯一)
        if target_index[0] != 'n':#如果是r或m，则更新n
            for img in self.imgs:
                if target_index == img.rm_index:
                    if checkvar.get():#勾选
                        self.n_img_num += 1
                        paste_x = ((self.n_img_num + 1) % 2) * self.SCROLL_WIDTH#每行最多两个，所以这里用余数来控制
                        paste_y = int((self.n_img_num + 1) / 2) * 33
                        _, canvasid= self.add_checkbox(self.n_canvas, img.pinyin, img.path, 'n' + img.rm_index,
                                                             paste_x, paste_y, varvalue=True)#n是用来标记是否在n_canvas中，varvalue=True表示默认勾选
                        self.n_index_to_canvasid['n' + img.rm_index] = canvasid#用id访问，以便之后删除n_canvas中的组件
                        self.lastclick_imgindex=img.rm_index#记录最后一次点击的img，以进一步操作
                        img.isshow = True
                        self.update_canvas()
                    else:
                        self.n_img_num -= 1
                        img.isshow = False
                        if self.lastclick_imgindex == img.rm_index:#如果最后一次点击的img被取消勾选，则记录为None
                            self.lastclick_imgindex=None
                        self.n_canvas.delete(self.n_index_to_canvasid['n' + img.rm_index])
                        #待优化，这里需要删除n_canvas中的checkbox
                        self.update_canvas()
        else:
            target_index = target_index[1:]#去掉n，n是用来标记是否在n_canvas中
            for img in self.imgs:
                if target_index == img.rm_index:
                    img.isshow = checkvar.get()
                    self.lastclick_imgindex=img.rm_index
                    #print(img.pinyin, img.isshow)
                    self.update_canvas()
        #print(checkvar._name, checkvar.get())

    def add_checkbox(self, canvas, text, imgpath, var_index, x, y, varvalue = False):
        frame = tk.Frame(canvas)

        checkvar = tk.IntVar(name=var_index, value=varvalue)
        check = tk.Checkbutton(frame, text=text, variable=checkvar, command=lambda: self.on_checkbox_changed(checkvar))
        check.pack(side=tk.LEFT, anchor=tk.W)
        # 加载图片并调整大小
        img = Image.open(imgpath)
        img = img.resize((int(img.width * 30 // img.height), 30))  # 保持宽高比，但高度为30
        photo = ImageTk.PhotoImage(img)
        label = tk.Label(frame, image=photo)
        label.image = photo  # 防止垃圾回收
        label.pack(side=tk.LEFT)

        # 将frame添加到canvas上
        canvas_id = canvas.create_window(x, y, window=frame, anchor=tk.NW)

        # 保留frame和canvas_id的引用，以便之后可以删除或操作它们
        return frame, canvas_id

    def loaddata(self):
        radicals_dir = INIT_DICT["RADICALS_DIR"]
        times = 0
        if os.path.exists(radicals_dir):
            for filename in os.listdir(radicals_dir):
                imgfile = os.path.join(radicals_dir, filename, 'img.png')
                if os.path.exists(imgfile):
                    self.imgs.append(IMG_MIXER(imgfile, filename, rm_index=f'r{times}', isradical=True))
                    times += 1
        #同上。我在考虑是否要合并这两个文件夹
        marks_dir = INIT_DICT["MARKS_DIR"]
        times = 0
        if os.path.exists(marks_dir):
            for filename in os.listdir(marks_dir):
                imgfile = os.path.join(marks_dir, filename, 'img.png')
                if os.path.exists(imgfile):
                    self.imgs.append(IMG_MIXER(imgfile, filename,  rm_index=f'm{times}', isradical=False))
                    times += 1

    def update_canvas(self):
        self.openedimages = []
        for img in self.imgs:
            if img.isshow:  # 如果该图片被勾选
                openedimg = img.imgopen()
                openedimg = ImageTk.PhotoImage(openedimg)
                self.canvas.create_image(img.x, img.y, image=openedimg, anchor=tk.NW)
                self.openedimages.append(openedimg)  # 防止图像被垃圾回收

        
        #photo = ImageTk.PhotoImage(self.current_image)
        #self.canvas.create_image(0, 0, image=photo, anchor=tk.NW)
        #self.canvas.image = photo  # 防止图像被垃圾回收

    def change_img_pos(self, event):
        value = event.get()
        for img in self.imgs:
            if self.lastclick_imgindex == img.rm_index:
                if event._name == 'c_entry_x':
                    try:
                        img.x += int(value)
                    except ValueError:
                        messagebox.showinfo(message="请输入数字（可以输入负数）")
                elif event._name == 'c_entry_y':
                    try:
                        img.y += int(value)
                    except ValueError:
                        messagebox.showinfo(message="请输入数字（可以输入负数）")
                elif event._name == 'c_entry_o':
                    try:
                        img.setrotatenum(int(value))
                    except ValueError:
                        messagebox.showinfo(message="请输入数字（可以输入负数）")
                elif event._name == 'c_entry_s':
                    try:
                        img.setsizemul(float(value), float(value))
                    except ValueError:
                        messagebox.showinfo(message="请输入数字（以1为标准，可以输入小数）")
                elif event._name == 'c_entry_sx':
                    try:
                        img.setsizemul(float(value), None)
                    except ValueError:
                        messagebox.showinfo(message="请输入数字（以1为标准，可以输入小数）")
                elif event._name == 'c_entry_sy':
                    try:
                        img.setsizemul(None, float(value))
                    except ValueError:
                        messagebox.showinfo(message="请输入数字（以1为标准，可以输入小数）")
                self.update_canvas()

    def on_click13(self, event):
        self.old_x = event.x
        self.old_y = event.y
    def on_click1_release(self, event):#左键拖动
        for img in self.imgs:
            if img.rm_index == self.lastclick_imgindex:
                img.x += event.x - self.old_x
                img.y += event.y - self.old_y
                self.update_canvas()
    def on_click3_release(self, event):#右键旋转，依照旋转角旋转，以鼠标点击点为旋转中心
        x_change = event.x - self.old_x
        y_change = event.y - self.old_y
        for img in self.imgs:
            if img.rm_index == self.lastclick_imgindex:
                img.setrotatenum(-math.atan2(y_change, x_change) / (math.pi/180))#弧度转角度
                self.update_canvas()
        
    def on_scroll(self, event):#滚轮放大缩小
        for img in self.imgs:
            if img.rm_index == self.lastclick_imgindex:
                img.setsizemul(img._xsizemul + event.delta / 1200, img._ysizemul + event.delta / 1200)
                self.update_canvas()

    def open_save_popup(self, none = None): #在此弹窗输入拼音即可保存图片
        popup = tk.Toplevel()
        popup.title("输入拼音后按回车键保存")
        entry = tk.Entry(popup)
        entry.pack()
        var_to_word = tk.IntVar(value=True)
        var_to_radical = tk.IntVar()
        var_to_mark = tk.IntVar()
        tk.Checkbutton(popup, text="成为单字(Alt+q)", variable=var_to_word).pack()
        tk.Checkbutton(popup, text="成为部首(Alt+w)", variable=var_to_radical).pack()
        tk.Checkbutton(popup, text="成为附标(Alt+e)", variable=var_to_mark).pack()
        extrainfo = ("单字:你可以直接使用\n部首:你可以稍后融合部首，以更快造字\n附标:你可以在部首融合处使用，也可以在作文时输入到上一个单词的头顶或底下等。" +
                     "通常，保存为附标时，你不需再保存为单字\n保存时，会自动将部首使用信息链接到新字/部/标中，你可以查看哪些字使用了哪些部首")
        tk.Button(popup, text="??", command=lambda: messagebox.askyesno("按回车键(Enter)保存", extrainfo)).pack(side=tk.RIGHT, pady=10)
        tk.Button(popup, text="取消(Alt+F4)", command=popup.destroy).pack(side=tk.LEFT, pady=10)
        entry.bind("<Return>", lambda e: self.save_image(entry, popup, var_to_word, var_to_radical, var_to_mark))
        var_invert = lambda var:var.set(not var.get())#翻转var的勾选
        entry.bind("<Alt-q>", lambda f: var_invert(var_to_word))
        entry.bind("<Alt-w>", lambda f: var_invert(var_to_radical))
        entry.bind("<Alt-e>", lambda f: var_invert(var_to_mark))
        entry.focus_set()# 自动将焦点设置到输入框上
        popup.mainloop()

    def save_image(self, entry, popup, vartoword, vartoradical, vartomark):
        imgpinyin = entry.get()
        istoward = vartoword.get()
        istoradical = vartoradical.get()
        istomark = vartomark.get()
        def a_to_data_of_used_rm(a_txt):#添加链接的信息到使用的部首的data.txt中，注意是使用的部首而非新创建的
            for img in self.imgs:#处理data.txt
                if img.isshow:
                    try:
                        with open(img.path.rsplit('\\', 1)[0] + '\\' + 'data.txt', "a") as file:
                            file.write(a_txt)
                    except Exception as e:
                        messagebox.showerror('创建部首与字间的链接失败', e)
        if not (istoward or istoradical or istomark):
            messagebox.showinfo(message="请至少选择一项\n按'??'查看帮助")
        elif not imgpinyin:
            messagebox.showinfo(message="请输入拼音\n单字建议用英文字母,部首和附标则可以用你的母语")
        elif '+' in imgpinyin:
            messagebox.showinfo(message="请不要使用'+'!")
        else:            
            save_image = Image.new('RGBA', (self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT), (255, 255, 255, 0))
            mixedData=save_image.getdata()#白色的像素数据
            mixedData = [list(tup) for tup in mixedData]#将像素数据转换为列表,0.06s
            #遍历所有显示的图片，将图片非白色数据加入到mixedData中
            #这是为了新图片的白色像素不干扰旧图片
            for img in self.imgs:
                if img.isshow:
                        imgload = Image.new('RGBA', (self.bytelength * self.CANVAS_WIDTH, self.CANVAS_HEIGHT), (255, 255, 255, 0))
                        imgload.paste(img.imgopen(), img.xy)#确保大小相同，并粘贴到指定位置
                        datas=imgload.getdata()#获取像素数据
                        times = 0
                        for item in datas:
                            if item[3] != 0:#如果该像素不是透明，则将该像素加入到mixedData中。0.04s/img
                                mixedData[times] = item
                            times += 1
            #将mixedData放入save_image中
            mixedData = [tuple(lst) for lst in mixedData]#0.05s
            save_image.putdata(mixedData)
            #保存
            if istoward:
                r_save_image = save_image.convert('RGB').resize([self.bytelength * INIT_DICT["D_SAVE_WIDTH"], INIT_DICT["D_SAVE_HEIGHT"]])#单字为小图片不带透明度
                #保存名：'拼音' + "+" + '字节长度（一位数)' (+ "-第几个同字节该拼音字")
                #使用的部首的data.txt中a+ : '\n' + 保存名
                file_path = INIT_DICT['FONTS_DIR'] + '\\' + imgpinyin + '+' + str(self.bytelength)
                if not os.path.exists(file_path + '.png'):
                    r_save_image.save(file_path + '.png')
                    a_to_data_of_used_rm('\n' + imgpinyin + '+' + str(self.bytelength))
                    WORDSDATA_DICT["SUM_OF_FONTS"]+=1
                    WORDSDATA_DICT["SUM_OF_WORDS"]+=1
                    popup.destroy()
                else:#处理拼音相同的情况：用"-"+'是第几个该拼音的字'区别。后期会大改这种按名字索引的方法
                    for i in range(2, 32767):
                        file_path_extra = file_path + '-' + str(i) + '.png'
                        if not os.path.exists(file_path_extra):
                            r_save_image.save(file_path_extra)
                            a_to_data_of_used_rm('\n' + imgpinyin + '+' + str(self.bytelength) + '-' + str(i))
                            WORDSDATA_DICT["SUM_OF_FONTS"]+=1
                            WORDSDATA_DICT["SUM_OF_WORDS"]+=1
                            popup.destroy()
                            break
            if istoradical:
                #会保存成部首
                # #使用的部首的data.txt中a+ : '\n' +  pinyin + '_r'
                file_path = INIT_DICT["RADICALS_DIR"] + '\\' + imgpinyin
                try:
                    os.makedirs(file_path, exist_ok=False)
                    save_image.save(file_path + '\\' + 'img.png')
                    with open(file_path + '\\' + 'data.txt', "w", encoding='utf-8') as file:#这是新创建的部首的data文件，别混淆
                        file.write(f"{imgpinyin}+{self.bytelength}:")
                        for img in self.imgs:
                            if img.isshow:
                                file.write(f"{img.rm_index[0]}{img.path.split('\\')[-2].rsplit('.', 1)[0]}+")#写入该字的构成部首，以+分割
                    a_to_data_of_used_rm('\n' + imgpinyin + '_r')
                    WORDSDATA_DICT["SUM_OF_RADICALS"]+=1
                    WORDSDATA_DICT["SUM_OF_WORDS"]+=1
                    popup.destroy()
                except FileExistsError:
                    messagebox.showerror("该名称已被取", "部首不支持重名，请换个名字或删除原部首")
                except Exception as e:
                    messagebox.showerror('Create Failed', e)
            if istomark:
                #会保存成附标
                # #使用的部首的data.txt中a+ : '\n' +  pinyin + '_m'
                file_path = INIT_DICT["MARKS_DIR"] + '\\' + imgpinyin
                try:
                    os.makedirs(file_path, exist_ok=False)
                    save_image.save(file_path + '\\' + 'img.png')
                    with open(file_path + '\\' + 'data.txt', "w", encoding='utf-8') as file:#
                        file.write(f"{imgpinyin}+{self.bytelength}:")
                        for img in self.imgs:
                            if img.isshow:
                                file.write(f"{img.rm_index[0]}{img.path.split('\\')[-2].rsplit('.', 1)[0]}+")#写入该字的构成部首，以+分割
                    a_to_data_of_used_rm('\n' + imgpinyin + '_m')
                    WORDSDATA_DICT["SUM_OF_MARKS"]+=1
                    WORDSDATA_DICT["SUM_OF_WORDS"]+=1
                    popup.destroy()
                except FileExistsError:
                    messagebox.showerror("该名称已被取", "附标不支持重名，请换个名字或删除原附标")
                except Exception as e:
                    messagebox.showerror('Create Failed', e)
            with open(WORDSDATA_FILE, "w", encoding='utf-8') as f:
                json.dump(WORDSDATA_DICT, f)
   
    def clear_canvas(self, none = None):#逻辑待改
        for img in self.imgs:
            img.isshow = False
        self.update_canvas()
        
    def lengthadd_canvas(self, none = None):
        if self.bytelength < self.MAXBYTE:
            self.bytelength+=1
            self.canvas.config(width = self.bytelength * self.CANVAS_WIDTH)
            self.update_canvas()
        
    def lengthminus_canvas(self, none = None):
        if self.bytelength > 1:
            self.bytelength-=1
            self.canvas.config(width = self.bytelength * self.CANVAS_WIDTH)
            self.update_canvas()

class Setting:
    def __init__(self, root):
        #一个设置窗口
        self.root = root
        self.root.title("设置")
        self.root.geometry("800x500")
        self.convert_to_transparent_btn = tk.Button(root, text="将某文件夹内所有.png图片\n白色像素转换为透明", command=lambda: self.to_transparent_popup()).pack(padx=40, pady=20, anchor=tk.NW)
        self.update_sum_words_btn = tk.Button(root, text="刷新字符总数", command=lambda: self.update_sum_words()).pack(padx=40, pady=20, anchor=tk.NW)
        self.txt_encoding_turn_btn = tk.Button(root, text="修改txt编码为utf-8", command=lambda: self.txt_encoding_turn_popup()).pack(padx=40, pady=20, anchor=tk.NW)

        tk.Button(root, text="返回", command=lambda:Appto(self.root, MainApp)).pack(padx=40, pady=20, anchor=tk.SW)
         
    def to_transparent_popup(self):#将白色像素转换为透明
        popup = tk.Toplevel(self.root)
        popup.title("将某路径下所有图片白色像素转换为透明")
        popup.geometry("300x150")
        popup.resizable(False, False)
        tk.Message(popup, text="请输入路径:", width=80).pack()
        Entry_dirpath = tk.Entry(popup, width=80)
        Entry_dirpath.pack(padx=10, pady=5)
        var_isdeliteorigin = tk.BooleanVar(value=False)
        tk.Checkbutton(popup, text="删除原图", variable=var_isdeliteorigin).pack()
        var_isdepthinto = tk.BooleanVar(value=False)
        tk.Checkbutton(popup, text="该文件夹下再穿透一层文件夹（用于部首之类）", variable=var_isdepthinto).pack()
        tk.Button(popup, text="确定", command=lambda: self.to_transparent_end(popup, Entry_dirpath.get(), var_isdeliteorigin.get(), var_isdepthinto.get())).pack()
        popup.mainloop()

    def to_transparent_end(self, popup, dirpath, isdeliteorigin, isdepthinto, isshowextrapopup = True, times = 0, succeedimg = [], isfirstcall=True):#将白色像素转换为透明
        is_ok_to_nowfile=True
        if isfirstcall:
            if isdeliteorigin == True:#如果删除原图，则询问是否确认
                is_ok_to_nowfile=messagebox.askyesno("确认?", f"确定将该文件夹 {dirpath} 内所有图片的白色像素转换为透明但不保留原图？\n此操作不可逆!")
            else:
                is_ok_to_nowfile=messagebox.askyesno("确认?", f"确定将该文件夹 {dirpath} 内所有图片的白色像素转换为透明？\n另保存为原名_transparent.png?")
        else:
            if isshowextrapopup:
                is_ok_to_nowfile=messagebox.askquestion("确认?", f"对 {dirpath} 文件夹操作？")
                if times >= 3 and times <= 5:
                    if messagebox.askyesno("提示！", f"是否隐藏后续的再次确认弹窗？\n此消息只显示3次!"):
                        isshowextrapopup = False

        if is_ok_to_nowfile:
            if os.path.exists(dirpath):
                for filename in os.listdir(dirpath):
                    if isdepthinto:#如果穿透一层目录，则遍历该目录下的所有文件夹
                        if os.path.isdir(os.path.join(dirpath, filename)):
                            #递归
                            isshowextrapopup, times, succeedimg  = self.to_transparent_end(popup, os.path.join(dirpath, filename), isdeliteorigin,#递归
                                                                   False, isshowextrapopup, times, succeedimg, False)
                            #递归
                    else:#否则只遍历该目录下的所有图片
                        if filename.endswith('.png'):
                            imgpath=os.path.join(dirpath, filename)
                            img=Image.open(imgpath)
                            if img.mode != 'RGBA':
                                img = img.convert('RGBA')
                            datas=img.getdata()
                            newData=[]
                            for item in datas:#将白色像素转换为透明
                                if item[0] == 255 and item[1] == 255 and item[2] == 255:
                                    newData.append((255, 255, 255, 0))
                                else:
                                    newData.append(item)
                            img.putdata(newData)
                            if isdeliteorigin == True:#如果删除原图，则直接覆盖
                                os.remove(imgpath)
                                img.save(imgpath)
                            else:#如果不删除原图，则保存+_transparent
                                imgpath = imgpath.rsplit('.', 1)[0] + '_transparent' + '.png'
                                img.save(imgpath)
                            succeedimg.append(filename)
                if not isfirstcall:
                    return isshowextrapopup, times+1, succeedimg #返回该目录下所有成功转换的图片
                if isfirstcall:#非递归或完成递归
                    messagebox.showinfo(f"成功转换{len(succeedimg)}张图片", succeedimg)
                    popup.destroy()
            else:
                messagebox.showerror('Path Error', '路径不存在')
        else:
            return isshowextrapopup,  times+1, succeedimg,

    def update_sum_words(self):#刷新字符总数
        if os.path.exists(INIT_DICT["FONTS_DIR"]):
            WORDSDATA_DICT["SUM_OF_FONTS"] = len(os.listdir(INIT_DICT["FONTS_DIR"]))
        if os.path.exists(INIT_DICT["RADICALS_DIR"]):
            WORDSDATA_DICT["SUM_OF_RADICALS"] = len(os.listdir(INIT_DICT["RADICALS_DIR"]))
        if os.path.exists(INIT_DICT["MARKS_DIR"]):
            WORDSDATA_DICT["SUM_OF_MARKS"] = len(os.listdir(INIT_DICT["MARKS_DIR"]))
        WORDSDATA_DICT["SUM_OF_WORDS"] = WORDSDATA_DICT["SUM_OF_FONTS"] + WORDSDATA_DICT["SUM_OF_RADICALS"] + WORDSDATA_DICT["SUM_OF_MARKS"]
        with open(WORDSDATA_FILE, 'w', encoding='utf-8') as f:
            json.dump(WORDSDATA_DICT, f)

    def create_initdata(self):#创建init_data.txt文件
        INIT_FILE
        if not os.path.exists(INIT_FILE.rsplit('\\', 1)[0]):
            os.makedirs(INIT_FILE.rsplit('\\', 1)[0])
        if not os.path.exists(INIT_FILE):
            with open(INIT_FILE, 'w') as f:
                for key, value in INIT_DICT.items():
                    f.write(key + '=' + str(value) + '\n')
        else:
            if messagebox.askyesno("文件已存在", "是否打开文件？"):
                os.system(INIT_FILE)

    def txt_encoding_turn_popup(self):#将txt文件编码格式转换为utf-8的弹窗
        popup = tk.Toplevel(self.root)
        popup.title("将某路径下txt文件编码格式转换为utf-8")
        popup.geometry("300x180")
        popup.resizable(False, False)
        tk.Message(popup, text="请输入路径:", width=80).pack()
        Entry_dirpath = tk.Entry(popup, width=80)
        Entry_dirpath.pack(padx=10, pady=5)
        tk.Message(popup, text='从何种格式转换至utf-8:', width=150).pack()
        Entry_encodefrom = tk.Entry(popup, width=80)
        Entry_encodefrom.pack(padx=10, pady=5)
        Entry_encodefrom.insert(0, 'gbk')
        var_isdepthinto = tk.BooleanVar(value=False)
        tk.Checkbutton(popup, text="该文件夹下再穿透一层文件夹（用于部首之类）", variable=var_isdepthinto).pack()
        tk.Button(popup, text="确定", command=lambda: self.txt_encoding_turn(popup, Entry_dirpath.get(), Entry_encodefrom.get().strip(), var_isdepthinto.get())).pack()
        popup.mainloop()

    def txt_encoding_turn(self, popup, dirpath, encodefrom = 'gbk', isneeddepthinto=False, isfirstcall=True, successtxt=None):
        if not messagebox.askyesno('再次确认', f"是否将{dirpath}路径下所有txt文件编码格式由{encodefrom}转换为utf-8?"):
                return
        try:
            ''.encode(encodefrom)
        except:
            if not messagebox.askyesno('Error', f'{encodefrom}可能不是有效的编码格式\n是否继续?'):
                return
        if successtxt is None:
            successtxt = []
        if isneeddepthinto:
            for filename in os.listdir(dirpath):
                if os.path.isdir(os.path.join(dirpath, filename)):
                    successtxt = self.txt_encoding_turn(popup, os.path.join(dirpath, filename), encodefrom, False, False, successtxt)
        if (isfirstcall and not isneeddepthinto) or (not isfirstcall):
            for filename in os.listdir(dirpath):
                if filename.endswith('.txt'):
                    with open(os.path.join(dirpath, filename), 'r', encoding=encodefrom) as f:
                        content = f.read()
                    with open(os.path.join(dirpath, filename), 'w', encoding='utf-8') as f:
                        f.write(content)
                    successtxt.append(filename + f"({encodefrom}→utf-8)")

            if not isfirstcall:
                return successtxt
        
        if isfirstcall:
            messagebox.showinfo(f"成功转换{len(successtxt)}个txt文件", "\n".join(successtxt))
            popup.destroy()

class TextEditor:  
    def __init__(self, root):  
        # 文本编辑窗口，用拼音或组成的部来输入字和附标，他们会首先在候选框中显示，然后通过键盘1-9点击确认，以显示在画布上
        #逻辑待完善
        self.root = root
        self.root.title("文本编辑")
        
        # 文本输入区域
        self.text_area = tk.Entry(root)
        self.text_area.pack(fill=tk.X, expand=True, padx=10)
        

        #俩画布都用的
        self.img_size_multiple = INIT_DICT['T_IMG_SIZE_MULTIPLE']
        self.CANVAS_WIDTH = INIT_DICT['T_CANVAS_WIDTH']
        
        # 候选框显示区域  
        self.persist_image_dict = {}#与输入结果匹配的字,显示在候选框内，[pinyin]=path。由于莫名原因，字典的排列十分规整，恰巧是我需要的方式（先a~z，再短到长），所以直接按字典默认顺序输出，等不对了再改吧
        self.persist_math_size = int(INIT_DICT['T_PERSIST_MATH_SIZE'] * INIT_DICT["T_IMG_SIZE_MULTIPLE"])#候选区数字大小
        self.persist_height = int((INIT_DICT["D_SAVE_HEIGHT"] + INIT_DICT["T_PERSIST_MATH_SIZE"] * 2.5) * INIT_DICT["T_IMG_SIZE_MULTIPLE"])#候选区高
        self.liftfontpage = 1#显示到的页数
        self.total_found_image_num = 0#总共找到的可以匹配的字数
        self.canvas_for_persist = Canvas(root, width=self.CANVAS_WIDTH, height=self.persist_height, bg='lightgrey')
        self.canvas_for_persist.pack(padx=10)

        # 画布1区域
        self.cursorplace = 0#光标的索引位置，用于对字列表指定位置操作
        self.CANVAS_HEIGHT = INIT_DICT['T_CANVAS_HEIGHT']
        self.canvas_image_paths = []#画布上图片位置的列表，逻辑待完善
        self.canvas = Canvas(root, width=self.CANVAS_WIDTH, height=self.CANVAS_HEIGHT, bg='white')  
        self.canvas.pack(padx=10, pady=10)
        self.current_image = Image.new('RGB', (self.CANVAS_WIDTH, self.CANVAS_HEIGHT), "white")
        self.had_warnned = False
        self.update_canvas()
  
        
        tk.Button(root, text="字变大", command=self.imginputbigger).pack(side=tk.RIGHT, padx=10, pady=10)
        tk.Button(root, text="字变小", command=self.imginputsmaller).pack(side=tk.RIGHT, padx=10, pady=10)
        tk.Button(root, text="返回", command=lambda:Appto(self.root, MainApp)).pack(side=tk.LEFT, padx=10, pady=10)
        tk.Button(root, text="保存", command=self.save_image).pack(side=tk.LEFT, padx=10, pady=10) 
        

        
        # 绑定文本区域的键盘事件  
        self.text_area.bind("<KeyRelease>", self.on_key_change)
        self.text_area.bind("<KeyPress>",self.on_key_about_to_change)
        # 初始化字体字典（这里应该是动态加载的）  
        self.font_dict = self.load_fonts()#[pinyin]=path
        
    def load_fonts(self):
        #加载data\fonts\目录下的所有字体图片，并构建一个字典。  
        #键为拼音（可能需要从文件名中提取），值为图片路径，字符宽的信息也存在于其中
        font_dict = {}
        fonts_dir = INIT_DICT["FONTS_DIR"]
        if os.path.exists(fonts_dir):
            for filename in os.listdir(fonts_dir): 
                if filename.endswith('.png'):  
                    # 假设文件名格式为“拼音+字节数.jpg”，这里简化处理只取拼音部分
                    parts = filename.rsplit('.', 1)[0].split('+')  
                    if len(parts) >= 2:  # 确保分割后至少有两个部分
                        pinyin = parts[0] + '+0' # 取第一部分作为拼音，同时添加一个数字确保每个pinyin是唯一的
                        times = 0
                        while True:
                            if pinyin not in font_dict: 
                                font_dict[pinyin] = os.path.join(fonts_dir, filename)
                                break
                            else:
                                times+=1
                                pinyin = re.sub(r'\d+$', str(times), pinyin) 
        else:
            messagebox.showerror("文件夹缺少", f"请创建一个存放字的图片的文件夹于{fonts_dir}")
        return font_dict
    
    def on_key_about_to_change(self, event):
        #该函数会在text_area改变前被调用。on_key_change则在其之后
        #此函数用于处理特殊输入
        #数字键1-9选定，0、小键盘箭头、PageDown等翻页，没有文本时按BackSpace撤回画布上的字
        pressed = event.keysym
        textlen = len(self.text_area.get())
        

        #选定1~9指定字事件:
        if pressed >= '1' and pressed <= '9':
            tarnum = int(pressed)
            timesnum = 0
            isfoundtar = False
            for key, value in self.persist_image_dict.items():
                #输入对应数字时，绘制图片至画布区，并清空输入框
                timesnum +=1
                if tarnum == timesnum:
                    isfoundtar = True
                    self.liftfontpage = 1
                    self.text_area.delete(0, 'end')
                    self.cursorplace += 1
                    self.canvas_image_paths.insert(self.cursorplace - 1, value)
            if isfoundtar == True:
                self.persist_image_dict.clear()
        #翻页事件:
        elif pressed == 'Up' or pressed == 'Prior':
            if self.liftfontpage == 1:
                self.liftfontpage = max(1, (self.total_found_image_num + 8) // 9)#0~9:1,10~18:2,19~27:3
            else:
                self.liftfontpage -= 1
        elif pressed == 'Down' or pressed == 'Next':
            if self.liftfontpage < (self.total_found_image_num + 8) // 9 : #0:0,1~9:1,10~18:2,19~27:3
                self.liftfontpage += 1
            else:
                self.liftfontpage = 1
        #文本框为空时，
        elif textlen == 0:
            #BackSpace改为撤回画布上的字:
            if pressed == 'BackSpace':
                if self.canvas_image_paths != [] and self.cursorplace != 0:
                    self.canvas_image_paths.pop(self.cursorplace - 1)
                    self.cursorplace -= 1
            #左右键改为移动画布1上的光标：
            elif pressed == 'Left':
                if self.cursorplace > 0:
                    self.cursorplace -= 1
            elif pressed == 'Right':
                if self.cursorplace < len(self.canvas_image_paths):
                    self.cursorplace += 1
        #若输入不是上述之一（输入字母之类），则翻回第一页
        else:
            self.liftfontpage = 1
        
    def on_key_change(self, event):
        #处理文本区域的键盘事件2，显示候选框，处理一般输入
        #在候选框改变后调用
        text = self.text_area.get()
        #由于on_key_about_to_change()不能删除最后打出的数字，此处额外删除一次
        if len(text) != 0:
            if text[-1] >= '1' and text[-1] <= '9':
                self.text_area.delete(len(text) -1,"end")
                text = text[:-1]
        #提取匹配的字的拼音和路径
        #这里是按字典内元素的插入顺序来访问，后期要修改
        self.persist_image_dict.clear()#杀死前候选框
        self.total_found_image_num=0
        num_in_pa=0
        pa=1
        for key, value in self.font_dict.items():
            if key.startswith(text):#匹配到
                self.total_found_image_num+=1
                num_in_pa+=1
                if num_in_pa == 10:
                    num_in_pa = 1
                    pa+=1
                if pa == self.liftfontpage:#只添加指定页数的字
                    self.persist_image_dict[key] = value
                
        self.update_canvas()

    def update_canvas(self):  
        self.update_canvas1()
        self.update_canvas_for_persist()
        
    def update_canvas_for_persist(self):#更新候选区画布
        #更新先前数据
        self.persist_math_size = int(INIT_DICT['T_PERSIST_MATH_SIZE'] * self.img_size_multiple)
        self.persist_height = int((INIT_DICT["D_SAVE_HEIGHT"] + INIT_DICT["T_PERSIST_MATH_SIZE"] * 2.5) * self.img_size_multiple)
        self.canvas_for_persist.delete('all')
        self.current_image = Image.new('RGB', (self.CANVAS_WIDTH, self.persist_height), "lightgray")
        #放数据
        persist_img_paths = list(self.persist_image_dict.values())
        img_xy = self.rowpictures(self.current_image, persist_img_paths, self.img_size_multiple, False, INIT_DICT['T_PERSIST_MARGIN'])
        photo = ImageTk.PhotoImage(self.current_image)
        self.canvas_for_persist.create_image(10, int(self.persist_math_size * 1.9), image=photo, anchor=tk.NW)
        times = 0#打印数字至每个字的左上脚
        for img_x, img_y in img_xy:
            times+=1
            if times == len(img_xy):#最后一个数据是最后一个图片右上的x,y位置，在canvas1里用到，此处舍弃不用
                break
            self.canvas_for_persist.create_text(img_x + 10, self.persist_math_size, text=f"{times}.", font=("Arial", self.persist_math_size))#第times个
            self.canvas_for_persist.create_text(int(self.CANVAS_WIDTH * 0.97), int(self.persist_height * 0.93), text=f"page{self.liftfontpage}", font=("Arial", int(self.persist_math_size * 0.7)))#页数
            pinyintext = ((list(self.persist_image_dict.keys())[times-1]).rsplit('+',1)[0])[:INIT_DICT['T_MAX_PERSIST_PINYIN_LENGTH']]#获取拼音，除去末尾数字，太长时截断
            self.canvas_for_persist.create_text(img_x + 20 + self.persist_math_size, int(self.persist_math_size * 1.8), text=pinyintext, font=("Arial", int(self.persist_math_size * 0.6)))#pinyin
        self.canvas_for_persist.image = photo#防止图像被垃圾回收

    def update_canvas1(self, isprintcursor = True): #更新画布1
        self.current_image = Image.new('RGB', (self.CANVAS_WIDTH, self.CANVAS_HEIGHT), "white")
        imgs_xy = self.rowpictures(self.current_image, self.canvas_image_paths, self.img_size_multiple, True)
        
        if isprintcursor:#打印cursor（光标）图片
            cursor_img_path = INIT_DICT['FONTS_DIR'] + "\\" + "-99SystemCursor+1.png"
            if os.path.exists(cursor_img_path):
                with Image.open(cursor_img_path) as img:
                    img_width, img_height = img.size
                    img_width *= self.img_size_multiple; img_height *= self.img_size_multiple
                    img_width = int(img_width); img_height = int(img_height)
                    img = img.resize((img_width, img_height))
                    self.current_image.paste(img, imgs_xy[self.cursorplace])
            else:
                if self.had_warnned == False:#只警告一遍。毕竟有时候可能确实不想要光标
                    self.had_warnned = True
                    messagebox.showwarning("图片缺失", f"请创建一个光标的.png图片于{INIT_DICT['D_SAVE_HEIGHT']}，推荐高度{cursor_img_path}，宽自定，约高十分之一")    

        photo = ImageTk.PhotoImage(self.current_image)  
        self.canvas.create_image(0, 0, image=photo, anchor=tk.NW)  
        self.canvas.image = photo#防止图像被垃圾回收
        
        
    def rowpictures(self, paste_img, img_paths: list, img_size_mul = 1 , isAllowLineFeed = True, margin = 0) -> list: #margin字间隔。isAllowLineFeed画布1用到True部分，候补区用到False部分    
        if img_paths != []:    
            # 初始化总高度和当前高度  
            total_height = 0  
            current_height = 0  
            current_width = 0  
      
            # 创建一个列表来保存每一行的图片  
            rows = []  
            current_row = []  
            #返回值.返回各个图片左上的x,y位置+最后一个图片右上的x,y位置
            imgs_xy = [(0,0)]
  
            # 遍历所有图片  
            for img_path in img_paths:  
                with Image.open(img_path) as img:  
                    # 获取图片缩放的宽度和高度
                    img_width, img_height = img.size
                    img_width *= img_size_mul; img_height *= img_size_mul
                    img_width = int(img_width); img_height = int(img_height)
                    # 检查当前行加上这张图片是否会超过宽度限制  
                    if current_width + img_width + margin > self.CANVAS_WIDTH:  
                        # 如果超过，则开始新行 或 结束粘贴
                        if isAllowLineFeed:
                            rows.append(current_row)  
                            current_row = []  
                            current_width = 0  
                            current_height += max(row[0][1] for row in rows if row) if rows else 0  # 计算上一行的高度
                            #imgs_xy.pop()
                            #imgs_xy.append((current_width, current_height))#修复了换行时的坐标
                        else:
                            break##
                    # 将图片添加到当前行  
                    current_row.append((img_width, img_height, img_path))  
                    current_width += img_width + margin
                    imgs_xy.append((current_width, current_height))
  
            # 不要忘记添加最后一行  
            if current_row:  
                rows.append(current_row)  
  
            # 计算新图像的总宽度和高度  
            max_width = max(sum(width + margin for width, _, _ in row) - margin for row in rows)  
            total_height += sum(max(height for _, height, _ in row) for row in rows)  
  
            # 创建一个新的白色背景图像  
            new_img = Image.new('RGB', (max_width, total_height), color = 'white')  
  
            # 粘贴图片到新图像  
            y_offset = 0  
            for row in rows:  
                x_offset = 0  
                for width, height, img_path in row:  
                    with Image.open(img_path) as img:  
                        resized_img = img.resize([width, height])#缩放图片
                        new_img.paste(resized_img, (x_offset, y_offset))  
                    x_offset += width + margin  
                y_offset += max(height for _, height, _ in row)  
  
            paste_img.paste(new_img,(0,0))
            return imgs_xy
        else:
            return([(0,0)])
        
    def save_image(self):  
        self.update_canvas1(False)#不更新光标地更新画布
        cut_image = self.current_image
        
        file_path = filedialog.asksaveasfilename(defaultextension=".png", filetypes=[("All files", "*.png"), ("All files", "*.*")], initialdir=INIT_DICT['DOCUMENTS_DIR'])  
        if file_path:  
            cut_image.save(file_path)


    def imginputbigger(self):
        self.img_size_multiple+=0.1
        self.update_canvas()
    def imginputsmaller(self):
        if self.img_size_multiple > 0.1:
            self.img_size_multiple-=0.1
        self.update_canvas()

class ListApp:
    def __init__(self, root):
        #一个窗口，用于显示所有字、部、标的列表：图片+拼音+其组成的部首与附标+你自己可填入的其他信息如该字的解释，也在此界面中直接修改。
        #有翻页功能。可以高级搜索，可以按不同方式排序。
        self.root = root  
        self.root.title("图片列表")
        self.root.geometry('1024x768')#窗口大小
        self.root.resizable(False, False)#窗口大小不可变
        self.EXCEL_FILE = INIT_DICT['EXCEL_FILE']
        self.MAXBYTELENTH = INIT_DICT['D_MAXBYTE']
        self.COLUMN_WIDTH_CMD = 5#命令列宽
        self.COLUMN_WIDTH_IMG = (INIT_DICT['D_SAVE_WIDTH'] / 12 * INIT_DICT['D_MAXBYTE'] + 0.1) // 2#图片列宽
        self.COLUMN_WIDTH_PINYIN = 12#拼音列宽
        self.COLUMN_WIDTH_SELFTYPE = 2.5#类型
        self.COLUMN_WIDTH_BYTELEN = 2.5#字节长列宽
        self.COLUMN_WIDTH_COMP = 15#组成列宽
        self.COLUMN_WIDTH_COMP_TO = 15#成字列宽
        self.COLUMN_WIDTH_DATE = 10#修改时间列宽
        self.ROW_HEIGHT = int(INIT_DICT['D_SAVE_HEIGHT'] * 3 / 8 + 2)#行高
        tk.Button(root, text="创建或覆盖Excel表", command=self.Excel_setup).pack()
        tk.Button(self.root, text="返回", command=lambda: Appto(self.root, MainApp)).pack()
    def Excel_setup(self):
        process_popup = tk.Toplevel(self.root)#弹出窗口
        process_popup.title("正在创建或覆盖Excel表...")
        process_popup.geometry('300x150')#窗口大小
        process_popup.resizable(False, False)#窗口大小不可变
        process_popup.grab_set()
        process_text = tk.StringVar()
        process_text.set("正在创建或覆盖Excel表...")
        tk.Label(process_popup, textvariable=process_text).pack()
        process_popup.update()
        if not os.path.exists(self.EXCEL_FILE):
            wb = Workbook()
            ws = wb.active
            ws['A1'] = '命令*'#输入以在读取时修改数据
            ws['B1'] = '图片'
            ws['C1'] = '拼音'
            ws['D1'] = '类'#r/m/f
            ws['E1'] = '宽'#字节
            ws['F1'] = '组成'#部首与附标
            ws['G1'] = '成字'#以其构成的字
            ws['H1'] = '修改时间'#修改时间
            ws['I1'] = '解释：（自定义）'#用户自定义
            ws.merge_cells('I1:K1')#合并单元格G1到H1
            ws.freeze_panes = 'E2'#冻结行
            ws.column_dimensions['A'].width = self.COLUMN_WIDTH_CMD
            ws.column_dimensions['B'].width = self.COLUMN_WIDTH_IMG
            ws.column_dimensions['C'].width = self.COLUMN_WIDTH_PINYIN
            ws.column_dimensions['D'].width = self.COLUMN_WIDTH_SELFTYPE
            ws.column_dimensions['E'].width = self.COLUMN_WIDTH_BYTELEN
            ws.column_dimensions['F'].width = self.COLUMN_WIDTH_COMP
            ws.column_dimensions['G'].width = self.COLUMN_WIDTH_COMP_TO
            ws.column_dimensions['H'].width = self.COLUMN_WIDTH_DATE
            wb.save(self.EXCEL_FILE)
        process_text.set("正在获取字的信息...")
        process_popup.update()
        radicals_list, marks_list, fonts_list = self.words_data_and_relation_get()
        process_text.set("正在写入Excel表...")
        process_popup.update()
        ##
        wb = load_workbook(self.EXCEL_FILE)
        ws = wb.active
        words_list = radicals_list + marks_list + fonts_list
        nowrow = ws.max_row + 1#行号
        for word in words_list:#imgfile, pinyin, selftype, bytelen, imgcomp, imgcomp_to, date
            img = Image.open(word[0])
            img = img.resize((min(word[3], INIT_DICT['D_MAXBYTE'] // 2) * INIT_DICT['D_SAVE_WIDTH'] // 2, INIT_DICT['D_SAVE_HEIGHT'] // 2))
            word[0] = word[0].rsplit('.', 1)[0] + 'excel.png'#图片
            img.save(word[0])
            xlimg = openpyxlImage(word[0])
            if len(word[4]) != 0:
                imgcomp = '\n'.join(word[4])
            else:
                imgcomp = ''
            if len(word[5]) != 0:
                imgcomp_to = '\n'.join(word[5])
            else:
                imgcomp_to = ''
            ws.append([None, None, word[1], word[2], word[3], imgcomp, imgcomp_to, word[6]])
            ws.row_dimensions[nowrow].height = self.ROW_HEIGHT
            ws[f'F{nowrow}'].alignment = Alignment(wrapText=True)#允许换行
            ws[f'G{nowrow}'].alignment = Alignment(wrapText=True)
            ws[f'H{nowrow}'].alignment = Alignment(wrapText=True)
            ws.add_image(xlimg, f'B{nowrow}')
            nowrow+=1
        wb.save(self.EXCEL_FILE)
        for word in words_list:#回收
            if word[0].endswith('excel.png'):#保险
                os.remove(word[0])
        process_text.set("Excel表创建或覆盖完成！\n此窗口将在2秒后自动关闭")
        process_popup.update()
        process_popup.after(2000, process_popup.destroy)

    def words_data_and_relation_get(self):
        radicals_list = []
        marks_list = []
        fonts_list = []

        radicals_dir = INIT_DICT["RADICALS_DIR"]
        if os.path.exists(radicals_dir):
            for dirname in os.listdir(radicals_dir):
                #a_radical = ['', '', '', 0, [], [], '']
                #imgfile, pinyin, selftype, bytelen, imgcomp, imgcomp_to, date
                a_radical = self.words_data_get(radicals_dir, dirname, '部')
                if a_radical != None:
                    radicals_list.append(a_radical)
        marks_dir = INIT_DICT["MARKS_DIR"]
        if os.path.exists(marks_dir):
            for dirname in os.listdir(marks_dir):
                a_mark = self.words_data_get(marks_dir, dirname, '附')
                if a_mark != None:
                    marks_list.append(a_mark)
        for filename in os.listdir(INIT_DICT["FONTS_DIR"]):
            imgfile = os.path.join(INIT_DICT["FONTS_DIR"], filename)
            if os.path.exists(imgfile):
                pinyin = filename.rsplit('.', 1)[0]#a+1_2
                bytelen = int(filename.rsplit('+', 1)[1][0])
                imgcomp = []
                imgcomp_to = []
                date = os.path.getmtime(imgfile)
                date = datetime.fromtimestamp(date).strftime('%Y\n%m-%d\n%H:%M:%S')
                selftype = '字'
                fonts_list.append([imgfile, pinyin, selftype, bytelen, imgcomp, imgcomp_to, date])


        fonts_list = self.words_relation_caculate(radicals_list, marks_list, fonts_list)#计算fonts的comp
        return radicals_list, marks_list, fonts_list

    def words_data_get(self, rootdir, dirname, selftype = '部'):
        ret_imgfile = ''; ret_pinyin = ''; ret_selftype = selftype; retbytelen = 0; ret_imgcomp = []; ret_imgcomp_to = []; ret_date = ''
        ret_imgfile = os.path.join(rootdir, dirname, 'img.png')
        if not os.path.exists(ret_imgfile):
            return None
        else:
            ret_date = os.path.getmtime(ret_imgfile)
        ret_pinyin = dirname
        datafile = os.path.join(rootdir, dirname, 'data.txt')
        if not os.path.exists(datafile):
            return None
        else:
            with open(datafile, 'r', encoding='utf-8') as file:
                ret_date = max(ret_date, os.path.getmtime(datafile))
                isfirstline = True
                for line in file:
                    if isfirstline:#例：test5+2:rtest2+rtest3+ -> int(2)、['test2部', 'test3部']
                        retbytelen, imgcomp = line.strip().rsplit(':', 1)
                        ret_imgcomp = imgcomp.split('+')[:-1]#不包括最后一个空的
                        for ret_imgcomp in ret_imgcomp:
                            if ret_imgcomp.startswith('r'):
                                ret_imgcomp = ret_imgcomp.lstrip('r')
                                ret_imgcomp = ret_imgcomp + '部'
                            elif ret_imgcomp.startswith('m'):
                                ret_imgcomp = ret_imgcomp.lstrip('m')
                                ret_imgcomp = ret_imgcomp + '附'
                        retbytelen = int(retbytelen.rsplit('+', 1)[1])
                        if isinstance(ret_imgcomp, str):#确保其是列表
                            ret_imgcomp = [ret_imgcomp]
                        isfirstline = False
                    else:#例：\ntest4+2\ntest5_r -> ['test4+2', 'test5部']
                        line = line.strip()
                        if line.endswith('_r'):
                            ret_imgcomp_to.append(line.rstrip('_r') + '部')
                        elif line.endswith('_m'):
                            ret_imgcomp_to.append(line.rstrip('_m') + '附')
                        else:
                            ret_imgcomp_to.append(line)
        ret_date = datetime.fromtimestamp(ret_date).strftime('%Y\n%m-%d\n%H:%M:%S')
        return [ret_imgfile, ret_pinyin, ret_selftype, retbytelen, ret_imgcomp, ret_imgcomp_to, ret_date]


    def words_relation_caculate(self, radicals_list, marks_list, fonts_list):
        #仅r和m的comp_to到f的comp需要计算。
        #imgfile, pinyin, selftype, bytelen, imgcomp, imgcomp_to, date
        pinyin_to_compfrom = {}
        for radical in radicals_list:
            for comp in radical[5]:
                if not (comp.endswith('部') and comp.endswith('附')):#指向了一个字
                    for times in range(255):#一个拼音会有多个部构成
                        pinyin = comp + '+' + str(times)
                        if pinyin not in pinyin_to_compfrom:
                            pinyin_to_compfrom[pinyin] = radical[1] + '部'
                            break
        for mark in marks_list:
            for comp in mark[5]:
                if not (comp.endswith('部') and comp.endswith('附')):
                    for times in range(255):#一个拼音会有多个部构成
                        pinyin = comp + '+' + str(times)
                        if pinyin not in pinyin_to_compfrom:
                            pinyin_to_compfrom[pinyin] = mark[1] + '附'
                            break

        for font in fonts_list:#字comp(from)
            for times in range(255):
                pinyin = font[1] + '+' + str(times)
                if pinyin in pinyin_to_compfrom:
                    font[4].append(pinyin_to_compfrom[pinyin])
                else:
                    break
        return fonts_list



        



def main():
    if not os.path.exists(os.path.join(PYDIR, "data")):
        os.makedirs(os.path.join(PYDIR, "data"))
    if not os.path.exists(WORDSDATA_FILE):
        with open(WORDSDATA_FILE, 'w', encoding='utf-8') as file:
            json.dump(WORDSDATA_DICT, file)
    if os.path.exists(WORDSDATA_FILE):
        with open(WORDSDATA_FILE, 'r', encoding='utf-8') as file:
            WORDSDATA_DICT.update(json.load(file))
    # if not os.path.exists(INIT_FILE):
    #     with open(INIT_FILE, 'w', encoding='utf-8') as file:
    #         for key in INIT_DICT:
    #             file.write(f"{key}={INIT_DICT[key]}\n")

    #用户自主修改的信息的提取
    if os.path.exists(INIT_FILE):
        with open(INIT_FILE, 'r', encoding='utf-8') as file:  
            for line in file:
                if line[0] != '#':#‘#’开头的直接忽略
                    parts = line.split('=',1)
                    if len(parts) == 2 and parts[0] in INIT_DICT:#确保一定有‘='号，且前面部分在字典内
                        #重写字典。非法数据忽略功能待添加
                        key = parts[0]
                        value = parts[1].strip()
                        if type(INIT_DICT[key]) == str:#保持数据类型与原字典的一样
                            if value[0] == '/' or value[0] == '\\':#若以/、\开头，自动在前添加py的路径
                                INIT_DICT[key] = PYDIR + value
                            else:
                                INIT_DICT[key] = value
                        elif type(INIT_DICT[key]) == int:
                            inttedvalue = 1
                            try:  
                                inttedvalue = int(value)  
                            except ValueError as e:  
                                messagebox.showerror(e ,f"请检查{INIT_FILE}，将这个错误信息修改为一个数字，或注释掉那一行来使用默认的数据")
                            INIT_DICT[key] = inttedvalue
                        else:
                            pass#其他数据类型
    else:
        messagebox.showwarning("未找到初始化配置用的文件" ,f"没有{INIT_FILE}。你仍可以自由使用，只是无法按照喜好修改部分基本数据")

    #.json的实现方法待添加:
    # # 用户自主修改的信息的提取  
    # if os.path.exists(INIT_FILE):
    #     with open(INIT_FILE, 'r', encoding='utf-8') as file:
    #         config = json.load(file)
    #         for key, value in config.items():
    #             if key in INIT_DICT:
    #                 if isinstance(INIT_DICT[key], str):
    #                     if isinstance(value, str) and (value.startswith('/') or value.startswith('\\')):#若以/、\开头，自动在前添加py的路径
    #                         value = os.path.join(PYDIR, value.lstrip('\\/'))
    #                     INIT_DICT[key] = value
    #                 elif isinstance(INIT_DICT[key], int):
    #                     try:
    #                         INIT_DICT[key] = int(value)
    #                     except ValueError as e:
    #                         messagebox.showerror(e ,f"请检查{INIT_FILE}，将这个错误信息修改为一个数字，或删除掉那一行来使用默认的数据")
    #                 # 如果需要处理其他类型，可以添加更多的elif分支
    #else:
    #    messagebox.showwarning("未找到初始化配置用的文件" ,f"没有{INIT_FILE}。你仍可以自由使用，只是无法按照喜好修改部分基本数据")

    #检查文件夹的存在
    for key, value in INIT_DICT.items():
        if type(value) == str:
            if '\\' in value or '/' in value:
                if not os.path.exists(value):
                    if key == 'EXCEL_FILE':#白名单
                        continue
                    messagebox.showerror("未找到指定文件", f"没有{value},你可以创建一个，或者在{INIT_FILE}自行修改该文件位置")

    root = tk.Tk()  
    app = MainApp(root)
    root.mainloop()







if __name__ == "__main__":
    main()
    


